Render this report with ~/spinal_cord_paper/scripts/Gg_devel_scWGCNA_module_analysis_render.sh.

library(Seurat)
Attaching SeuratObject
library(WGCNA)
Loading required package: dynamicTreeCut
Loading required package: fastcluster

Attaching package: ‘fastcluster’

The following object is masked from ‘package:stats’:

    hclust



Attaching package: ‘WGCNA’

The following object is masked from ‘package:stats’:

    cor
library(tidyr)
library(ggplot2)
library(stringr)
library(patchwork)
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ──────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ tibble  3.1.8      ✔ dplyr   1.0.10
✔ readr   1.4.0      ✔ forcats 0.5.1 
✔ purrr   0.3.4      
── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(cowplot)

Attaching package: ‘cowplot’

The following object is masked from ‘package:patchwork’:

    align_plots
library(pheatmap)
library(gridExtra)

Attaching package: ‘gridExtra’

The following object is masked from ‘package:dplyr’:

    combine
source("~/Neuraltube/scripts/heatmap4.R")

Load individual seurat and test WGCNA data

The individual data sets are the Day 5 (Gg_D05_ctrl_seurat_070323), Day 7 (Gg_D07_ctrl_seurat_070323), and Day 10 (Gg_ctrl_1_seurat_070323) chicken spinal cord sets. The test WGCNA data are the modules calculated on the integrated data set of all three stages.

se_path <- c("Gg_D05_ctrl_seurat_070323",
             "Gg_D07_ctrl_seurat_070323",
             "Gg_ctrl_1_seurat_070323")

clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv") %>% 
  rename(broad = broad_cluster) %>% 
  select(-marker)

Order of the broad clusters for plotting purposes.

broad_order <- c("progenitors",
      "FP",
      "RP",
      "FP/RP",
      "neurons",
      "OPC",
      "MFOL",
      "pericytes",
      "microglia",
      "blood",
      "vasculature"
      )
my.ses <- list()
col_table <- list()
ord_levels <- list()

for (i in seq(se_path)) {
  # load the data sets
  my.se <- readRDS(paste0("~/spinal_cord_paper/data/", se_path[i], ".rds"))
  annot <- read.csv(list.files("~/spinal_cord_paper/annotations",
                               pattern = str_remove(se_path[i], "_seurat_\\d{6}"),
                               full.names = TRUE))
  
  if(length(table(annot$number)) != length(table(my.se$seurat_clusters))) {
     stop("Number of clusters must be identical!")
  }
  
  # rename for left join
  annot <- annot %>% 
    mutate(fine = paste(fine, number, sep = "_")) %>% 
    mutate(number = factor(number, levels = 1:nrow(annot))) %>% 
    rename(seurat_clusters = number) 
  
  # cluster order for vln plots
  ord_levels[[i]] <- annot$fine[order(match(annot$broad, broad_order))]
  
  # create index for color coding
  col_table[[i]] <- annot %>%
    left_join(clust_col, by = "broad") %>% 
    select(c("fine", "color"))
  
  # add cluster annotation to meta data
  my.se@meta.data <- my.se@meta.data %>% 
    rownames_to_column("rowname") %>% 
    left_join(annot, by = "seurat_clusters") %>% 
    mutate(fine = factor(fine, levels = annot$fine)) %>% 
    column_to_rownames("rowname")
  
  my.ses[[i]] <- my.se

}

names(my.ses) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
names(col_table) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
names(ord_levels) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")

rm(my.se, annot)
# The reference WGCNA data. We can have several, if we want to test many at the same time
WGCNA_data = list()
WGCNA_data[[1]] = readRDS("~/spinal_cord_paper/output/Gg_devel_int_scWGCNA_250723.rds")
my.wsub =list()
my.wsub[[1]]= c(1:22)

# the name of each sample, as they appear in my.files and in the metadata of the combined object
my.samplenames = c("D05", "D07", "D10")

# The subset of clusters in each of the corresponding samples
my.subset=list(c(1:24),
               c(1:26),
               c(1:22))

# This is just to add a little bit more sense to the modules, so that we don't get just a color. Corresponds to WGCNA_data
my.modulenames = list()
my.modulenames[[1]] = c(1:22)

#Subset the seurat objects if needed as defined above
for (i in 1:length(my.ses)) {
  my.ses[[i]] = subset(my.ses[[i]], idents = my.subset[[i]])
}

# Take only genes that are present in the all samples
my.dcs = list()

# We go trhough each WGCNA data
for(i in 1:length(WGCNA_data)) {
  #We start with complete modules
  my.dc = WGCNA_data[[i]]$dynamicCols
  
  #Remove the few genes that are not found in the other datasets
  my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[1]]@assays$RNA@data))]
  my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[2]]@assays$RNA@data))]
  my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[3]]@assays$RNA@data))]
  
  my.dc = my.dc[which(my.dc %in% names(table(WGCNA_data[[i]]$dynamicCols))[my.wsub[[i]]])]
  
  my.dcs[[i]] = my.dc
  
}

Module gene correlation

Here, we do a correlation matrix / heatmap, to see which cell clusters group togheter. This helps us to make more detailed dotplots.
This part of the script can still be used to compare several WGCNA datasets in parallel.

# broad cluster color table
all_col <- do.call(rbind, col_table) %>% 
  rownames_to_column("sample") %>% 
  mutate(sample = substr(sample, 1, 3)) %>% 
  mutate(sample_celltype = paste(sample, fine, sep = "_")) %>% 
  select(c("color", "sample_celltype", "sample"))
all_col$sample
 [1] "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05" "D05"
[25] "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07" "D07"
[49] "D07" "D07" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10" "D10"

spearman correlation heatmap

annotations


# names and colors for the heatmap annotation
annot_name <- data.frame(
  "Celltypes" = all_col$sample_celltype,
  "Sample"    = all_col$sample,
  row.names = all_col$sample_celltype)
  

pheat_col_table <- do.call(rbind, col_table) %>% 
  rownames_to_column("sample") %>% 
  mutate(sample = substr(sample, 1,11)) %>% 
  mutate(fine = paste(sample, fine, sep = "_"))

# match color table with annotation
pheat_col_table <- pheat_col_table[match(annot_name$Celltypes, pheat_col_table$fine),]

annot_col <- list(
  Celltypes = pheat_col_table$color,
  Sample = c(D05 = "#A4A4A4",
             D07 = "#515151",
             D10 = "#000000")
  )

names(annot_col[[1]]) <- annot_name$Celltypes

heatmap of module pseudobulk average expression

Load the integrated data set

The integrated data set on which the WGCNA is calculated. We plot it, split by the original cell types from the three original samples.

Avg. module exp. by stage

tSNE DimPlots showing the average expression of each module by stage.

AE over time

We plot the average expression of each module in the three stages and the 5 broad cell type clusters present in all 3 stages.

mod_annot <- read.csv("~/spinal_cord_paper/annotations/Gg_devel_int_scWGCNA_module_annotation.csv") %>%
  dplyr::mutate(module = str_replace_all(module, "\\d{1,2}\\_", "AE"))

meta <- list()

for (i in seq(my.ses)) {
  meta[[i]] <- my.ses[[i]]@meta.data %>%
    tibble::rownames_to_column("cell_ID") %>%
    dplyr::mutate(cell_ID = paste0(cell_ID, "_", i)) %>%
    dplyr::select(c("cell_ID", "broad"))
}

meta <- do.call(rbind, meta)

# mean average expression by stage
mean_AE <- avg.mod.eigengenes %>%
  tibble::rownames_to_column("cell_ID") %>%
  dplyr::mutate(stage = stringr::str_sub(cell_ID, -1)) %>%
  dplyr::mutate(stage = factor(stage, levels = c(1:3), labels = c("D05", "D07", "D10"))) %>%
  dplyr::left_join(meta, by = "cell_ID") %>%
  tibble::column_to_rownames("cell_ID") %>%
  tidyr::unite("stage_cl", stage, broad, sep = "_") %>%
  dplyr::group_by(stage_cl) %>%
  dplyr::summarise_each(mean) %>%
  dplyr::ungroup() %>%
  gather(key="module", value = "AE", -stage_cl) %>%
  dplyr::left_join(mod_annot[, c(1,3)], by = "module") %>%
  tidyr::separate("stage_cl", c("stage", "broad"), sep = "_", remove = FALSE) 
Warning: `summarise_each_()` was deprecated in dplyr 0.7.0.
ℹ Please use `across()` instead.
ℹ The deprecated feature was likely used in the dplyr package.
  Please report the issue at <https://github.com/tidyverse/dplyr/issues>.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
# %>%
  # dplyr::filter(broad %in% c("progenitors", "neurons", "RP", "FP", "pericytes"))

labels_dotplot <- stringr::str_remove(modules_in_order, "^AE")
names(labels_dotplot) <- modules_in_order

plot_clusters <- c("progenitors", "neurons", "RP", "FP", "pericytes", "OPC", "MFOL", "microglia", "blood")

mean_mod <- ggplot(data = mean_AE,
  aes(
    x = stage,
    y = AE,
    color = factor(broad, levels = plot_clusters),
    group = broad,
    label = annotation
    )
  ) +
  geom_line() +
  geom_point() +
  scale_color_manual(values = clust_col$color[match(plot_clusters, clust_col$broad)]) +
  theme_bw() +
  # facet wrap with reordered factors
  facet_wrap(vars(factor(module, levels = unique(mean_AE$module)[module_order])),
             scales = "free_y",
             nrow = 4,
             ncol = 6) +
  labs(color = "broad") +
  ggtitle("Average module expression by stage")

plotly::ggplotly(mean_mod)

VlnPlots of avg. module exp. by stage and seurat cluster

colored by module

# reorder seurat clusters
for (i in seq(my.ses)) {
  my.ses[[i]]$seurat_clusters <- factor(
  my.ses[[i]]$seurat_clusters,
  levels = levels(my.ses[[i]]$seurat_clusters)[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]
  )

}

vplots <- list()

for (i in seq(my.ses)) {
  
  mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
  
  vplots[[i]] <- VlnPlot(
        my.ses[[i]],
        features = mods[module_order],
        group.by = "seurat_clusters",
        stack = TRUE, flip = TRUE,
        cols = substring(mods, 3)[module_order]) +
    theme(legend.position = "none") +
    geom_hline(yintercept = 0, lty = "dashed")
  
}

vplots[[1]]
vplots[[2]]

vplots[[3]]

colored by cell type

clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv")

vplots_id <- list()

for (i in seq(my.ses)) {
    
  mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
  
  vplots_id[[i]] <- VlnPlot(
    my.ses[[i]],
    features = mods[module_order],
    group.by = "seurat_clusters",
    stack = TRUE, flip = TRUE,
    fill.by = "ident",
    cols = col_table[[i]]$color[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]) +
    theme(legend.position = "none") +
    geom_hline(yintercept = 0, lty = "dashed")
}

vplots_id[[1]]
vplots_id[[2]]

vplots_id[[3]]

MN modules

For the figures we specifically select the two MN modules and plot them as Vln plots.

avg. module exp. by stage

v1 <- VlnPlot(my.sec,
        features = mods[module_order], 
        group.by = "orig.ident")
v1

pdf("~/spinal_cord_paper/figures/Fig_1_AE_by_stage.pdf", height = 20, width = 20)
v1

module GO terms barplots

# Load the top 50 (by limma::topGO) Go term table list from scWGCNA_Gg_devel_int.Rmd
goterms <- readRDS("~/spinal_cord_paper/output/Gg_devel_int_module_GOTerms_250723.rds")
# Load the devel module annotations 
modules <- read.csv("~/spinal_cord_paper/annotations/Gg_devel_int_scWGCNA_module_annotation.csv")
# Set list names as module number and name (e.g. "1_black")
names(goterms) <- modules$module

plot_list <- list()

for (i in seq(goterms)) {
  # Select the darkred module (motor neuron/transmembrane transport)
  toplot <- goterms[[i]]
  terms <- as.character(toplot$Term)
  
  #Plot the barplot!
  plot_list[[i]] <- ggplot(toplot, aes(x=Term, y=-log10(P.DE), fill=Ont)) +
    geom_bar(stat="identity") +
    scale_fill_manual(values = str_remove(names(goterms)[i], "^\\d{1,2}_")) +
    theme(axis.text.x = element_text(angle = 90, hjust = 1),
          axis.text.y = element_text(size = 5)) +
    labs(title=paste0("Top GOTerm of module ", names(goterms)[i])) +
    coord_flip() +
    geom_hline(yintercept = -log10(0.05), lty = "dashed") +
    theme_cowplot()
}

rm(toplot, terms)

names(plot_list) <- modules$module

# Export PDF
pdf("~/spinal_cord_paper/figures/GO_term_barplot_scWGCNA_Gg_devel_modules.pdf", width = 14, height = 7)
for (i in seq(goterms)) {
  grid.arrange(plot_list[[i]])
}
dev.off()
# Date and time of Rendering
Sys.time()

sessionInfo()
LS0tCnRpdGxlOiAiRGV2ZWxfaW50IFdHQ05BIG1vZHVsZXMgZXhwcmVzc2lvbiBpbiBOVCBENSwgRDcsIGFuZCBEMTAgc2FtcGxlcyIKYXV0aG9yOiAiRmFiaW8gU2FjaGVyIgpkYXRlOiAiMDQuMDguMjAyMyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDgKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KClJlbmRlciB0aGlzIHJlcG9ydCB3aXRoIH4vc3BpbmFsX2NvcmRfcGFwZXIvc2NyaXB0cy9HZ19kZXZlbF9zY1dHQ05BX21vZHVsZV9hbmFseXNpc19yZW5kZXIuc2guCgpgYGB7ciBzZXR1cH0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoV0dDTkEpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShncmlkRXh0cmEpCnNvdXJjZSgifi9OZXVyYWx0dWJlL3NjcmlwdHMvaGVhdG1hcDQuUiIpCgpgYGAKCiMgTG9hZCBpbmRpdmlkdWFsIHNldXJhdCBhbmQgdGVzdCBXR0NOQSBkYXRhCgpUaGUgaW5kaXZpZHVhbCBkYXRhIHNldHMgYXJlIHRoZSBEYXkgNSAoR2dfRDA1X2N0cmxfc2V1cmF0XzA3MDMyMyksIERheSA3IChHZ19EMDdfY3RybF9zZXVyYXRfMDcwMzIzKSwgYW5kIERheSAxMCAoR2dfY3RybF8xX3NldXJhdF8wNzAzMjMpIGNoaWNrZW4gc3BpbmFsIGNvcmQgc2V0cy4gVGhlIHRlc3QgV0dDTkEgZGF0YSBhcmUgdGhlIG1vZHVsZXMgY2FsY3VsYXRlZCBvbiB0aGUgaW50ZWdyYXRlZCBkYXRhIHNldCBvZiBhbGwgdGhyZWUgc3RhZ2VzLgoKYGBge3IgZGF0YS1zZXRzfQpzZV9wYXRoIDwtIGMoIkdnX0QwNV9jdHJsX3NldXJhdF8wNzAzMjMiLAogICAgICAgICAgICAgIkdnX0QwN19jdHJsX3NldXJhdF8wNzAzMjMiLAogICAgICAgICAgICAgIkdnX2N0cmxfMV9zZXVyYXRfMDcwMzIzIikKCmNsdXN0X2NvbCA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9icm9hZF9jbHVzdGVyX21hcmtlcl9jb2xvcnMuY3N2IikgJT4lIAogIHJlbmFtZShicm9hZCA9IGJyb2FkX2NsdXN0ZXIpICU+JSAKICBzZWxlY3QoLW1hcmtlcikKCmBgYAoKT3JkZXIgb2YgdGhlIGJyb2FkIGNsdXN0ZXJzIGZvciBwbG90dGluZyBwdXJwb3Nlcy4KCmBgYHtyIG9yZGVyaW5nfQpicm9hZF9vcmRlciA8LSBjKCJwcm9nZW5pdG9ycyIsCiAgICAgICJGUCIsCiAgICAgICJSUCIsCiAgICAgICJGUC9SUCIsCiAgICAgICJuZXVyb25zIiwKICAgICAgIk9QQyIsCiAgICAgICJNRk9MIiwKICAgICAgInBlcmljeXRlcyIsCiAgICAgICJtaWNyb2dsaWEiLAogICAgICAiYmxvb2QiLAogICAgICAidmFzY3VsYXR1cmUiCiAgICAgICkKYGBgCgoKYGBge3Igc2V1cmF0LW9iamVjdHMtYW5kLWFubm90YXRpb25zfQpteS5zZXMgPC0gbGlzdCgpCmNvbF90YWJsZSA8LSBsaXN0KCkKb3JkX2xldmVscyA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEoc2VfcGF0aCkpIHsKICAjIGxvYWQgdGhlIGRhdGEgc2V0cwogIG15LnNlIDwtIHJlYWRSRFMocGFzdGUwKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2RhdGEvIiwgc2VfcGF0aFtpXSwgIi5yZHMiKSkKICBhbm5vdCA8LSByZWFkLmNzdihsaXN0LmZpbGVzKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSBzdHJfcmVtb3ZlKHNlX3BhdGhbaV0sICJfc2V1cmF0X1xcZHs2fSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFRSVUUpKQogIAogIGlmKGxlbmd0aCh0YWJsZShhbm5vdCRudW1iZXIpKSAhPSBsZW5ndGgodGFibGUobXkuc2Ukc2V1cmF0X2NsdXN0ZXJzKSkpIHsKICAgICBzdG9wKCJOdW1iZXIgb2YgY2x1c3RlcnMgbXVzdCBiZSBpZGVudGljYWwhIikKICB9CiAgCiAgIyByZW5hbWUgZm9yIGxlZnQgam9pbgogIGFubm90IDwtIGFubm90ICU+JSAKICAgIG11dGF0ZShmaW5lID0gcGFzdGUoZmluZSwgbnVtYmVyLCBzZXAgPSAiXyIpKSAlPiUgCiAgICBtdXRhdGUobnVtYmVyID0gZmFjdG9yKG51bWJlciwgbGV2ZWxzID0gMTpucm93KGFubm90KSkpICU+JSAKICAgIHJlbmFtZShzZXVyYXRfY2x1c3RlcnMgPSBudW1iZXIpIAogIAogICMgY2x1c3RlciBvcmRlciBmb3IgdmxuIHBsb3RzCiAgb3JkX2xldmVsc1tbaV1dIDwtIGFubm90JGZpbmVbb3JkZXIobWF0Y2goYW5ub3QkYnJvYWQsIGJyb2FkX29yZGVyKSldCiAgCiAgIyBjcmVhdGUgaW5kZXggZm9yIGNvbG9yIGNvZGluZwogIGNvbF90YWJsZVtbaV1dIDwtIGFubm90ICU+JQogICAgbGVmdF9qb2luKGNsdXN0X2NvbCwgYnkgPSAiYnJvYWQiKSAlPiUgCiAgICBzZWxlY3QoYygiZmluZSIsICJjb2xvciIpKQogIAogICMgYWRkIGNsdXN0ZXIgYW5ub3RhdGlvbiB0byBtZXRhIGRhdGEKICBteS5zZUBtZXRhLmRhdGEgPC0gbXkuc2VAbWV0YS5kYXRhICU+JSAKICAgIHJvd25hbWVzX3RvX2NvbHVtbigicm93bmFtZSIpICU+JSAKICAgIGxlZnRfam9pbihhbm5vdCwgYnkgPSAic2V1cmF0X2NsdXN0ZXJzIikgJT4lIAogICAgbXV0YXRlKGZpbmUgPSBmYWN0b3IoZmluZSwgbGV2ZWxzID0gYW5ub3QkZmluZSkpICU+JSAKICAgIGNvbHVtbl90b19yb3duYW1lcygicm93bmFtZSIpCiAgCiAgbXkuc2VzW1tpXV0gPC0gbXkuc2UKCn0KCm5hbWVzKG15LnNlcykgPC0gYygiRDA1IiwgIkQwNyIsICJEMTAiKQpuYW1lcyhjb2xfdGFibGUpIDwtIGMoIkQwNSIsICJEMDciLCAiRDEwIikKbmFtZXMob3JkX2xldmVscykgPC0gYygiRDA1IiwgIkQwNyIsICJEMTAiKQoKcm0obXkuc2UsIGFubm90KQpgYGAKCmBgYHtyIGlucHV0IGRhdGEsIGVjaG89VFJVRX0KIyBUaGUgcmVmZXJlbmNlIFdHQ05BIGRhdGEuIFdlIGNhbiBoYXZlIHNldmVyYWwsIGlmIHdlIHdhbnQgdG8gdGVzdCBtYW55IGF0IHRoZSBzYW1lIHRpbWUKV0dDTkFfZGF0YSA9IGxpc3QoKQpXR0NOQV9kYXRhW1sxXV0gPSByZWFkUkRTKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL291dHB1dC9HZ19kZXZlbF9pbnRfc2NXR0NOQV8yNTA3MjMucmRzIikKbXkud3N1YiA9bGlzdCgpCm15LndzdWJbWzFdXT0gYygxOjIyKQoKIyB0aGUgbmFtZSBvZiBlYWNoIHNhbXBsZSwgYXMgdGhleSBhcHBlYXIgaW4gbXkuZmlsZXMgYW5kIGluIHRoZSBtZXRhZGF0YSBvZiB0aGUgY29tYmluZWQgb2JqZWN0Cm15LnNhbXBsZW5hbWVzID0gYygiRDA1IiwgIkQwNyIsICJEMTAiKQoKIyBUaGUgc3Vic2V0IG9mIGNsdXN0ZXJzIGluIGVhY2ggb2YgdGhlIGNvcnJlc3BvbmRpbmcgc2FtcGxlcwpteS5zdWJzZXQ9bGlzdChjKDE6MjQpLAogICAgICAgICAgICAgICBjKDE6MjYpLAogICAgICAgICAgICAgICBjKDE6MjIpKQoKIyBUaGlzIGlzIGp1c3QgdG8gYWRkIGEgbGl0dGxlIGJpdCBtb3JlIHNlbnNlIHRvIHRoZSBtb2R1bGVzLCBzbyB0aGF0IHdlIGRvbid0IGdldCBqdXN0IGEgY29sb3IuIENvcnJlc3BvbmRzIHRvIFdHQ05BX2RhdGEKbXkubW9kdWxlbmFtZXMgPSBsaXN0KCkKbXkubW9kdWxlbmFtZXNbWzFdXSA9IGMoMToyMikKCmBgYAoKYGBge3IgcHJlLXByb2Nlc3MsIGVjaG89VFJVRX0KCiNTdWJzZXQgdGhlIHNldXJhdCBvYmplY3RzIGlmIG5lZWRlZCBhcyBkZWZpbmVkIGFib3ZlCmZvciAoaSBpbiAxOmxlbmd0aChteS5zZXMpKSB7CiAgbXkuc2VzW1tpXV0gPSBzdWJzZXQobXkuc2VzW1tpXV0sIGlkZW50cyA9IG15LnN1YnNldFtbaV1dKQp9CgojIFRha2Ugb25seSBnZW5lcyB0aGF0IGFyZSBwcmVzZW50IGluIHRoZSBhbGwgc2FtcGxlcwpteS5kY3MgPSBsaXN0KCkKCiMgV2UgZ28gdHJob3VnaCBlYWNoIFdHQ05BIGRhdGEKZm9yKGkgaW4gMTpsZW5ndGgoV0dDTkFfZGF0YSkpIHsKICAjV2Ugc3RhcnQgd2l0aCBjb21wbGV0ZSBtb2R1bGVzCiAgbXkuZGMgPSBXR0NOQV9kYXRhW1tpXV0kZHluYW1pY0NvbHMKICAKICAjUmVtb3ZlIHRoZSBmZXcgZ2VuZXMgdGhhdCBhcmUgbm90IGZvdW5kIGluIHRoZSBvdGhlciBkYXRhc2V0cwogIG15LmRjID0gbXkuZGNbd2hpY2gobmFtZXMobXkuZGMpICVpbiUgcm93bmFtZXMobXkuc2VzW1sxXV1AYXNzYXlzJFJOQUBkYXRhKSldCiAgbXkuZGMgPSBteS5kY1t3aGljaChuYW1lcyhteS5kYykgJWluJSByb3duYW1lcyhteS5zZXNbWzJdXUBhc3NheXMkUk5BQGRhdGEpKV0KICBteS5kYyA9IG15LmRjW3doaWNoKG5hbWVzKG15LmRjKSAlaW4lIHJvd25hbWVzKG15LnNlc1tbM11dQGFzc2F5cyRSTkFAZGF0YSkpXQogIAogIG15LmRjID0gbXkuZGNbd2hpY2gobXkuZGMgJWluJSBuYW1lcyh0YWJsZShXR0NOQV9kYXRhW1tpXV0kZHluYW1pY0NvbHMpKVtteS53c3ViW1tpXV1dKV0KICAKICBteS5kY3NbW2ldXSA9IG15LmRjCiAgCn0KYGBgCgojIE1vZHVsZSBnZW5lIGNvcnJlbGF0aW9uCgpIZXJlLCB3ZSBkbyBhIGNvcnJlbGF0aW9uIG1hdHJpeCAvIGhlYXRtYXAsIHRvIHNlZSB3aGljaCBjZWxsIGNsdXN0ZXJzIGdyb3VwIHRvZ2hldGVyLiBUaGlzIGhlbHBzIHVzIHRvIG1ha2UgbW9yZSBkZXRhaWxlZCBkb3RwbG90cy4gIApUaGlzIHBhcnQgb2YgdGhlIHNjcmlwdCBjYW4gc3RpbGwgYmUgdXNlZCB0byBjb21wYXJlIHNldmVyYWwgV0dDTkEgZGF0YXNldHMgaW4gcGFyYWxsZWwuICAKCmBgYHtyfQojIGJyb2FkIGNsdXN0ZXIgY29sb3IgdGFibGUKYWxsX2NvbCA8LSBkby5jYWxsKHJiaW5kLCBjb2xfdGFibGUpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oInNhbXBsZSIpICU+JSAKICBtdXRhdGUoc2FtcGxlID0gc3Vic3RyKHNhbXBsZSwgMSwgMykpICU+JSAKICBtdXRhdGUoc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUoc2FtcGxlLCBmaW5lLCBzZXAgPSAiXyIpKSAlPiUgCiAgc2VsZWN0KGMoImNvbG9yIiwgInNhbXBsZV9jZWxsdHlwZSIsICJzYW1wbGUiKSkKYGBgCgpgYGB7ciBtb2R1bGVfZXhwcmVzc2lvbl9kYXRhLCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0yMH0KCiMgdGFrZSB0aGUgbm9ybWFsaXplZCBkYXRhLCBvZiBlYWNoIHNpbmdsZS1jZWxsIG9iamVjdC4KbXkuZGF0RXhwciA9IGxpc3QoKQoKIyBHbyB0cm91Z2ggZWFjaCBzZXVyYXQgb2JqZWN0CmZvciAoaSBpbiAxOmxlbmd0aChteS5zZXMpKSB7CiBteS5kYXRFeHByW1tpXV0gPSBkYXRhLmZyYW1lKG15LnNlc1tbaV1dQGFzc2F5cyRSTkFAZGF0YSkKfQoKIyBDYWxjdWxhdGUsIGZvciBldmVyeSBjZWxsLCBpbiBldmVyeSBzYW1wbGUsIHRoZSBhdmVyYWdlIGV4cHJlc3Npb24gb2YgZWFjaCBvZiB0aGUgbW9kdWxlcy4KbXkuTUVzID0gbGlzdCgpCgojIEZvciBlYWNoIHNldCBvZiBXR0NOQSBtb2R1bGVzCmZvciAoaSBpbiAxOmxlbmd0aChXR0NOQV9kYXRhKSkgewogIAogICMgTWFrZSBhIG5ldyBsaXN0CiAgbXkuTUVzW1tpXV0gPSBsaXN0KCkKICAKICAjIFBvcHVsYXRlIHdpdGggZGF0YSBmcmFtZXMgZnJvbSBlYWNoIHNldXJhdCBvYmplY3QKICBmb3IgKGogaW4gMTpsZW5ndGgobXkuc2VzKSkgewogICAgCiAgICAjIERhdGEgZnJhbWUgb2YgYXZlcmFnZSBtb2R1bGUgZXhwcmVzc2lvbiwgcGVyIGNlbGwuCiAgICBteS5NRXNbW2ldXVtbal1dID0gbW9kdWxlRWlnZW5nZW5lcyh0KG15LmRhdEV4cHJbW2pdXVtuYW1lcyhteS5kY3NbW2ldXSksXSksIGNvbG9ycyA9IG15LmRjc1tbaV1dKQogIH0KfQoKI0NhbGN1bGF0ZSBhdmVyYWdlIG9mIGV4cHJlc3Npb24sIHBlciBzYW1wbGUgYW5kIGNlbGwgY2x1c3RlcgpteS5tb2RhdmcgPSBsaXN0KCkKCmZvciAoaSBpbiAxOmxlbmd0aChXR0NOQV9kYXRhKSkgewogIAogIG15Lm1vZGF2Z1tbaV1dID0gbGlzdCgpCiAgCiAgZm9yIChqIGluIDE6bGVuZ3RoKG15LnNlcykpIHsKICAgIAogICAgI01ha2UgdGhlIG1lYW4gcGVyIGNlbGwgY2x1c3RlcnMKICAgIG15Lm1vZGF2Z1tbaV1dW1tqXV0gPSBhZ2dyZWdhdGUoCiAgICAgIG15Lk1Fc1tbaV1dW1tqXV0kYXZlcmFnZUV4cHIsCiAgICAgIGxpc3QobXkuc2VzW1tqXV1AbWV0YS5kYXRhJHNldXJhdF9jbHVzdGVycyksCiAgICAgIG1lYW4KICAgICAgKQogICAgIyBHaXZlIHRoZSBjZWxsIGNsdXN0ZXJzIG1lYW5pbmdmdWwgbmFtZXMKICAgIHJvd25hbWVzKG15Lm1vZGF2Z1tbaV1dW1tqXV0pID0gcGFzdGUwKAogICAgICBteS5zYW1wbGVuYW1lc1tqXSwKICAgICAgIl8iLAogICAgICBsZXZlbHMobXkuc2VzW1tqXV1AbWV0YS5kYXRhJGZpbmUpW215LnN1YnNldFtbal1dXQogICAgICApCiAgICB9Cn0KCgoKI0dhdGhlciBkYXRhIHRvIHBsb3QKCm15LndnbWF0ID0gbGlzdCgpCgpmb3IgKGkgaW4gMTpsZW5ndGgoV0dDTkFfZGF0YSkpIHsKICAKICAjYmluZCB0aGUgZXhwcmVzc2lvbiBkYXRhIGludG8gb25lIGRhdGFmcmFtZQogIG15LndnbWF0W1tpXV0gPSBkYXRhLnRhYmxlOjpyYmluZGxpc3QobXkubW9kYXZnW1tpXV0pCiAgI0dldCByaWQgb2YgdGhlIGV4dHJhIGNvbHVtbgogIG15LndnbWF0W1tpXV0gPSBkYXRhLmZyYW1lKG15LndnbWF0W1tpXV1bLC0xXSkKICAjUmVzdG9yZSB0aGUgcm93bmFtZXMKICByb3duYW1lcyhteS53Z21hdFtbaV1dKSA9IHVubGlzdChzYXBwbHkobXkubW9kYXZnW1tpXV0sIHJvd25hbWVzKSkKICAKfQoKCgoKI0dldCBhIGRhdGFmcmFtZSB3aXRoIGFubm90YXRpb25zIGZvciBhbGwgdGhlIHNhbXBsZXMgYW5kIGNvbG9ycyB3ZSBuZWVkLgpteS5tZXRhbSA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogIG15Lm1ldGFtW1tpXV0gPC0gbXkuc2VzW1tpXV1bW11dCiAgcm93bmFtZXMobXkubWV0YW1bW2ldXSkgPC0gcGFzdGUwKHJvd25hbWVzKG15Lm1ldGFtW1tpXV0pLCAiXyIsIGkpCn0KbXkubWV0YW0gPC0gZG8uY2FsbChyYmluZCwgbXkubWV0YW0pCgpteS5tZXRhbSRvcmlnLmlkZW50IDwtIHN0cl9yZXBsYWNlX2FsbChteS5tZXRhbSRvcmlnLmlkZW50LCBwYXR0ZXJuID0gICJHZ19EMDVfY3RybCIsICJEMDUiKQpteS5tZXRhbSRvcmlnLmlkZW50IDwtIHN0cl9yZXBsYWNlX2FsbChteS5tZXRhbSRvcmlnLmlkZW50LCBwYXR0ZXJuID0gICJHZ19EMDdfY3RybCIsICJEMDciKQpteS5tZXRhbSRvcmlnLmlkZW50IDwtIHN0cl9yZXBsYWNlX2FsbChteS5tZXRhbSRvcmlnLmlkZW50LCBwYXR0ZXJuID0gICJHZ19jdHJsXzEiLCAiRDEwIikKCiMgbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUwKHN1YnN0cihteS5tZXRhbSRvcmlnLmlkZW50LDcsOSksIl8iLG15Lm1ldGFtJHNldXJhdF9jbHVzdGVycykKbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlID0gcGFzdGUwKG15Lm1ldGFtJG9yaWcuaWRlbnQsICJfIiwgbXkubWV0YW0kZmluZSkKCm15Lm1ldGFtIDwtIG15Lm1ldGFtICU+JSAKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiY2VsbF9JRCIpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oYWxsX2NvbCwgYnkgPSAic2FtcGxlX2NlbGx0eXBlIikgJT4lCiAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXModmFyID0gImNlbGxfSUQiKQoKCiMgZ2V0IHNhbXBsZSBjb2xvcnMKbXkuY29sc20gPSBjKCJncmV5IiwgImdyZXkzMCIsICJibGFjayIpCm5hbWVzKG15LmNvbHNtKSA8LSBjKCJEMDUiLCAiRDA3IiwgIkQxMCIpCiAKI1RoZSBjb2xvcnMgZm9yIHRoZSBzYW1wbGVzIGFuZCBjbHVzdGVycy4gRmlyc3QgdGhlIGNsb3Nlc3QgdG8gdGhlIGhlYXRtYXAuIEFkZCBhIHdoaXRlIHNwYWNlIHRvIGVhc3kgdGhlIGV5ZSBhbmQgbWFrZSBsZXNzIGNvbmZ1c3NpbmcKbXkuaGVhdGNvbHMgPWxpc3QoKQpmb3IgKGkgaW4gMTpsZW5ndGgobXkud2dtYXQpKSB7CiAgbXkuaGVhdGNvbHNbW2ldXSA9IGFzLm1hdHJpeChkYXRhLmZyYW1lKAogICAgY2x1c3Rlcj0gYXMuY2hhcmFjdGVyKGFsbF9jb2wkY29sb3JbbWF0Y2gocm93bmFtZXMobXkud2dtYXRbW2ldXSksIGFsbF9jb2wkc2FtcGxlX2NlbGx0eXBlKV0pLAogICAgIi4iID0gIndoaXRlIiwKICAgIHNhbXBsZT0gYXMuY2hhcmFjdGVyKG15LmNvbHNtW21hdGNoKGFsbF9jb2wkc2FtcGxlLCBuYW1lcyhteS5jb2xzbSkpXSkpCiAgICApCn0KCnJtKG15LmRhdEV4cHIsIG15Lk1FcywgbXkuZGNzKQpgYGAKCiMjIHNwZWFybWFuIGNvcnJlbGF0aW9uIGhlYXRtYXAKCiMjIyBhbm5vdGF0aW9ucwoKYGBge3IgYW5ub3QtbGlzdH0KCiMgbmFtZXMgYW5kIGNvbG9ycyBmb3IgdGhlIGhlYXRtYXAgYW5ub3RhdGlvbgphbm5vdF9uYW1lIDwtIGRhdGEuZnJhbWUoCiAgIkNlbGx0eXBlcyIgPSBhbGxfY29sJHNhbXBsZV9jZWxsdHlwZSwKICAiU2FtcGxlIiAgICA9IGFsbF9jb2wkc2FtcGxlLAogIHJvdy5uYW1lcyA9IGFsbF9jb2wkc2FtcGxlX2NlbGx0eXBlKQogIAoKcGhlYXRfY29sX3RhYmxlIDwtIGRvLmNhbGwocmJpbmQsIGNvbF90YWJsZSkgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigic2FtcGxlIikgJT4lIAogIG11dGF0ZShzYW1wbGUgPSBzdWJzdHIoc2FtcGxlLCAxLDMpKSAlPiUgCiAgbXV0YXRlKGZpbmUgPSBwYXN0ZShzYW1wbGUsIGZpbmUsIHNlcCA9ICJfIikpCgojIG1hdGNoIGNvbG9yIHRhYmxlIHdpdGggYW5ub3RhdGlvbgpwaGVhdF9jb2xfdGFibGUgPC0gcGhlYXRfY29sX3RhYmxlW21hdGNoKGFubm90X25hbWUkQ2VsbHR5cGVzLCBwaGVhdF9jb2xfdGFibGUkZmluZSksXQoKYW5ub3RfY29sIDwtIGxpc3QoCiAgQ2VsbHR5cGVzID0gcGhlYXRfY29sX3RhYmxlJGNvbG9yLAogIFNhbXBsZSA9IGMoRDA1ID0gIiNBNEE0QTQiLAogICAgICAgICAgICAgRDA3ID0gIiM1MTUxNTEiLAogICAgICAgICAgICAgRDEwID0gIiMwMDAwMDAiKQogICkKCm5hbWVzKGFubm90X2NvbFtbMV1dKSA8LSBhbm5vdF9uYW1lJENlbGx0eXBlcwoKYGBgCgpgYGB7ciwgIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xM30KaGVhdF9jb2wgPC0gY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCJkb2RnZXJibHVlNCIsImRvZGdlcmJsdWUiLCAid2hpdGUiLCAicmVkIiwgImRhcmtyZWQiKSkKCiNDYWxjdWxhdGUgdGhlIGRpc3RhbmNlIDEtY29yLCBhbmQgdGhlbiBjYWxjdWxhdGUgZGVuZG9ncmFtcyB0byBjbHVzdGVyIHRoZSBjbHVzdGVycwpteS5oY2x1c3QgPSBoY2x1c3QoYXMuZGlzdCgxLWNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSkpCgojIGxvd2VyIGxpbWl0IHRvIHNjYWxlIGNvbG9yIGJhcgpsb3dfbGltaXQgPC0gMTAwIC0gYWJzKHJvdW5kKHJhbmdlKGNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSlbMV0qMTAwKSkKCnBoZWF0bWFwKGNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSxyZXZDID0gVCwKICAgICAgICAgICAgICAgICBtYWluID0gInNwZWFybWFuIGNvcnJlbGF0aW9uIG9mIGF2ZXJhZ2UgbW9kdWxlIGVpZ2VuZ2VuZSBleHByZXNzaW9uIGJ5IGNsdXN0ZXJcbiBEZW5kcm9ncmFtIGJhc2VkIG9uICdkaXN0YW5jZSAxLWNvciciLAogICAgICAgICAgICAgICAgIGZvbnRzaXplID0gOCwgIAogICAgICAgICAgICAgICAgIGNvbG9yID0gaGVhdF9jb2woMjAwKVtsb3dfbGltaXQ6MjAwXSwKICAgICAgICAgICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBteS5oY2x1c3QsCiAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2xzID0gbXkuaGNsdXN0LAogICAgICAgICAgICAgICAgIHRyZWVoZWlnaHRfcm93ID0gMCwgCiAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBhbm5vdF9uYW1lLAogICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gYW5ub3RfY29sLAogICAgICAgICAgICAgICAgIGFubm90YXRpb25fbGVnZW5kID0gRiwKICAgICAgICAgICAgICAgICBib3JkZXJfY29sb3IgPSBOQQogICAgICAgICAgICAgICAgICkKCmNvbGJhciA8LSBjKG1pbihjb3IodChteS53Z21hdFtbMV1dKSwgbWV0aG9kID0gInNwZWFybWFuIikpLAogICAgICAgICAgICBtYXgoY29yKHQobXkud2dtYXRbWzFdXSksIG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkKCiNDYWxjdWxhdGUgdGhlIGRpc3RhbmNlIDEtY29yLCBhbmQgdGhlbiBjYWxjdWxhdGUgZGVuZG9ncmFtcyB0byBjbHVzdGVyIHRoZSBjbHVzdGVycwpteS5oY2x1c3QgPSBhcy5kZW5kcm9ncmFtKGhjbHVzdChhcy5kaXN0KDEtY29yKHQobXkud2dtYXRbWzFdXSksIG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkpCgojUGxvdCEKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfbW9kdWxlX2dlbmVfaGVhdG1hcF9zcGVhcm1hbi5wZGYiLCB3aWR0aCA9IDExLCBoZWlnaHQgPSAxMSkKaGVhdG1hcC40KGNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSwKICAgICAgICAgIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZG9kZ2VyYmx1ZTQiLCJkb2RnZXJibHVlIiwgIndoaXRlIiwgInJlZCIsICJkYXJrcmVkIikpKG4gPSAxMDAwKSwKICAgICAgICAgIHRyYWNlPSJub25lIiwKICAgICAgICAgIGxoZWk9YygwLjIsMS42KSwKICAgICAgICAgIGx3aWQ9YygwLjIsMS42KSwKICAgICAgICAgIHNjYWxlID0gIm5vbmUiLG1hcmdpbnMgPSBjKDEwLDEwKSwKICAgICAgICAgIGNleENvbCA9IDAuNzUsCiAgICAgICAgICByZXZDID0gVFJVRSwKICAgICAgICAgIENvbFNpZGVDb2xvcnMgPSBteS5oZWF0Y29sc1tbMV1dLAogICAgICAgICAgUm93U2lkZUNvbG9ycyA9IHQobXkuaGVhdGNvbHNbWzFdXVssMzoxXSksUm93diA9IG15LmhjbHVzdCwgQ29sdiA9IG15LmhjbHVzdCkKCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9tb2R1bGVfZ2VuZV9oZWF0bWFwX2NvbG9yX2tleV9zcGVhcm1hbi5wZGYiLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMCkKIyBoZWF0bWFwIHNpbmNlIGNvbG9yIGtleSBpcyBtaXNzaW5nIGluIGZpcnN0IGhlYXRtYXAKaGVhdG1hcC40KHJiaW5kKGNvbGJhciwgY29sYmFyKSwKICAgICAgICAgIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZG9kZ2VyYmx1ZTQiLCJkb2RnZXJibHVlIiwgIndoaXRlIiwgInJlZCIsICJkYXJrcmVkIikpKG4gPSAxMDAwKSwKICAgICAgICAgIHRyYWNlPSJub25lIiwKICAgICAgICAgIGxoZWk9YygwLjIsMS42KSwKICAgICAgICAgIGx3aWQ9YygwLjIsMS42KSwKICAgICAgICAgIHNjYWxlID0gIm5vbmUiLG1hcmdpbnMgPSBjKDEwLDEwKSwKICAgICAgICAgIGNleENvbCA9IDAuNzUpCgpgYGAKCiMjIGhlYXRtYXAgb2YgbW9kdWxlIHBzZXVkb2J1bGsgYXZlcmFnZSBleHByZXNzaW9uCgpgYGB7ciwgIGZpZy5oZWlnaHQ9MTUsIGZpZy53aWR0aD0xMH0KIyBtb2R1bGUgY29sb3JzCm15LmNvbGNvbHMgPSBsaXN0KCkKCmZvciAoaSBpbiAxOmxlbmd0aChteS53Z21hdCkpIHsKICAgIG15LmNvbGNvbHNbW2ldXSA9IGFzLm1hdHJpeChuYW1lcyh0YWJsZShXR0NOQV9kYXRhW1tpXV0kZHluYW1pY0NvbHMpKSkKfQoKYXZnLm1vZC5laWdlbmdlbmVzIDwtIFdHQ05BX2RhdGFbWzFdXSRzYy5NRUxpc3QkYXZlcmFnZUV4cHIKcm0oV0dDTkFfZGF0YSkKCmh0bXAgPC0gaGVhdG1hcC40KGFzLm1hdHJpeChteS53Z21hdFtbMV1dKSwKICAgICAgICAgIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZG9kZ2VyYmx1ZTQiLCJkb2RnZXJibHVlIiwgIndoaXRlIiwgInJlZCIsICJkYXJrcmVkIikpKG4gPSAxMDAwKSwKICAgICAgICAgIHRyYWNlPSJub25lIiwKICAgICAgICAgIHNjYWxlID0gInJvdyIsCiAgICAgICAgICBtYXJnaW5zID0gYygxMCwxMCksCiAgICAgICAgICBDb2xTaWRlQ29sb3JzID0gbXkuY29sY29sc1tbMV1dLCAKICAgICAgICAgIFJvd1NpZGVDb2xvcnMgPSB0KG15LmhlYXRjb2xzW1sxXV0pKQoKbW9kdWxlX29yZGVyIDwtIGh0bXBbWyJjb2xJbmQiXV0KCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0RldmVsX21vZHVsZV92X2NsdXN0ZXJzX2hlYXRtYXAucGRmIiwgd2lkdGggPSA3LCBoZWlnaHQgPSAxMCkKaGVhdG1hcC40KGFzLm1hdHJpeChteS53Z21hdFtbMV1dKSwKICAgICAgICAgIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZG9kZ2VyYmx1ZTQiLCJkb2RnZXJibHVlIiwgIndoaXRlIiwgInJlZCIsICJkYXJrcmVkIikpKG4gPSAxMDAwKSwKICAgICAgICAgIHRyYWNlPSJub25lIiwKICAgICAgICAgIHNjYWxlID0gInJvdyIsCiAgICAgICAgICBtYXJnaW5zID0gYygxMCwxMCksCiAgICAgICAgICBDb2xTaWRlQ29sb3JzID0gbXkuY29sY29sc1tbMV1dLCAKICAgICAgICAgIFJvd1NpZGVDb2xvcnMgPSB0KG15LmhlYXRjb2xzW1sxXV0pKQoKYGBgCgoKCiMgTG9hZCB0aGUgaW50ZWdyYXRlZCBkYXRhIHNldAoKVGhlIGludGVncmF0ZWQgZGF0YSBzZXQgb24gd2hpY2ggdGhlIFdHQ05BIGlzIGNhbGN1bGF0ZWQuIFdlIHBsb3QgaXQsIHNwbGl0IGJ5IHRoZSBvcmlnaW5hbCBjZWxsIHR5cGVzIGZyb20gdGhlIHRocmVlIG9yaWdpbmFsIHNhbXBsZXMuIAoKYGBge3IgZGltcGxvdHMsIGZpZy53aWR0aD0xMH0KIyBUaGlzIGlzIGEgZmlsZSBvZiBhbGwgdGhlIGNvbWJpbmVkIG1vdXNlIGRhdGFzZXRzLCBub3JtYWxpemVkIGFuZCBzdWNoLgpteS5zZWMgPSByZWFkUkRTKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2RhdGEvR2dfZGV2ZWxfaW50X3NldXJhdF8yNTA3MjMucmRzIikKCmlkZW50aWNhbChyb3duYW1lcyhteS5tZXRhbSksIGNvbG5hbWVzKG15LnNlYykpCgojV2hpY2ggV0dDTkEgZGF0YXNldCBhcmUgd2UgdXNpbmc/Cm15LlcgPSAxCgojQ2hvb3NlIGhvdyBtYW55IGdyb3VwcyBvZiBjZWxsIGNsdXN0ZXJzIHdlIHdhbnQgdG8gdXNlLiBCYXNlZCBvbiB0aGUgZGlzdGFuY2UgY2x1c3RlcmluZyBmcm9tIGFib3ZlCm15LmNsY2wgPSBjdXRyZWUoaGNsdXN0KGFzLmRpc3QoMS1jb3IodChteS53Z21hdFtbbXkuV11dKSkpKSwgayA9IDUpCgpteS5tZXRhbSRzYW1wbGVfY2VsbHR5cGUgPC0gZmFjdG9yKG15Lm1ldGFtJHNhbXBsZV9jZWxsdHlwZSwgbGV2ZWxzID0gbmFtZXMobXkuY2xjbCkpCiNTZXQgdGhlIGlkZW50aXRpZXMgb2YgdGhlIGludGVncmF0ZWQgZGF0YSwgdG8gdGhlIGFubm90YXRlZCBjbHVzdGVycwpteS5zZWMgPSBTZXRJZGVudChteS5zZWMsIHZhbHVlID0gbXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlKQoKcDEgPC0gRGltUGxvdCgKICBteS5zZWMsCiAgcmVkdWN0aW9uID0gInRzbmUiLAogIGxhYmVsID0gVFJVRSwKICByZXBlbCA9IFRSVUUsCiAgY29scyA9IG15LmhlYXRjb2xzW1sxXV1bLCJjbHVzdGVyIl0sCiAgc3BsaXQuYnkgPSAib3JpZy5pZGVudCIKICApICsgCiAgTm9MZWdlbmQoKQoKcDEKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0RldmVsX3NwbGl0X3RzbmUucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSAxNSkKI1Bsb3Qgc3BsaXQgdHNuZQpwMQpgYGAKCiMgQXZnLiBtb2R1bGUgZXhwLiBieSBzdGFnZQoKdFNORSBEaW1QbG90cyBzaG93aW5nIHRoZSBhdmVyYWdlIGV4cHJlc3Npb24gb2YgZWFjaCBtb2R1bGUgYnkgc3RhZ2UuCgpgYGB7ciBBRSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTQwfQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgIyBwcmVwYXJlIGF2ZXJhZ2UgZXhwcmVzc2lvbiB0YWJsZQogIHRtcCA8LSBhdmcubW9kLmVpZ2VuZ2VuZXMgJT4lCiAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiY2VsbF9JRCIpICU+JQogICAgZHBseXI6OmZpbHRlcihncmVwbChwYXN0ZTAoIl8iLCBpLCAiJCIpLCBjZWxsX0lEKSkgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGNlbGxfSUQgPSBzdHJpbmdyOjpzdHJfcmVtb3ZlX2FsbChjZWxsX0lELCBwYXN0ZTAoIl8iLCBpKSkpICU+JQogICAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXMoImNlbGxfSUQiKQogIAogIGlkZW50aWNhbChyb3duYW1lcyh0bXApLCBjb2xuYW1lcyhteS5zZXNbW2ldXSkpCiAgIyBhZGQgbWV0YSBkYXRhIHRvIHRoZSBzZXVyYXQgb2JqZWN0cwogIG15LnNlc1tbaV1dIDwtIEFkZE1ldGFEYXRhKG15LnNlc1tbaV1dLCB0bXApCn0KCiNtYXggYW5kIG1pbiBleHByZXNzaW9uIHBlciBtb2R1bGUgKGNvbHVtbiBtYXgpCm1vZF9tYXggPC0gYXBwbHkoYXZnLm1vZC5laWdlbmdlbmVzLCBNQVJHSU4gPSAyLCBGVU4gPSBtYXgpW21vZHVsZV9vcmRlcl0KbW9kX21pbiA8LSBhcHBseShhdmcubW9kLmVpZ2VuZ2VuZXMsIE1BUkdJTiA9IDIsIEZVTiA9IG1pbilbbW9kdWxlX29yZGVyXQoKbW9kcGxvdHMgPC0gbGlzdCgpCm1vZHBsb3RzW1sxXV0gPC0gbGlzdCgpCm1vZHBsb3RzW1syXV0gPC0gbGlzdCgpCm1vZHBsb3RzW1szXV0gPC0gbGlzdCgpCgptb2R1bGVzX2luX29yZGVyIDwtIGNvbG5hbWVzKHRtcClbbW9kdWxlX29yZGVyXQoKIyBwbG90IHRoZSBtb2R1bGVzIHNwbGl0IHRvIHRoZSBzdGFnZXMKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgZm9yIChqIGluIHNlcShuY29sKHRtcCkpKSB7CiAgCiAgICBtb2RwbG90c1tbaV1dW1tqXV0gIDwtIEZlYXR1cmVQbG90KAogICAgICBteS5zZXNbW2ldXSwKICAgICAgZmVhdHVyZXMgPSBtb2R1bGVzX2luX29yZGVyW2pdLAogICAgICByZWR1Y3Rpb24gPSAidHNuZSIsCiAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCBzdWJzdHJpbmcobW9kdWxlc19pbl9vcmRlcltqXSwgMykpCiAgICAgICkgKwogICAgICBnZ3RpdGxlKHN0cmluZ3I6OnN0cl9yZW1vdmUobW9kdWxlc19pbl9vcmRlcltqXSwiXkFFIikpICsKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93PSJpdm9yeTIiLCBoaWdoPXN1YnN0cmluZyhtb2R1bGVzX2luX29yZGVyW2pdLCAzKSwgI2NvbG9ycyBpbiB0aGUgc2NhbGUKICAgICAgICAgICAgICAgICAjIGJyZWFrcz1zZXEobW9kX21pbltqXSwgbW9kX21heFtqXSwgMC4xKSwgI2JyZWFrcyBpbiB0aGUgc2NhbGUgYmFyCiAgICAgICAgICAgICAgICAgbGltaXRzPWMobW9kX21pbltqXSwgbW9kX21heFtqXSkpICNzYW1lIGxpbWl0cyBmb3IgcGxvdHMKCiAgICAKICAgIH0KfQoKdG1wIDwtIGMobW9kcGxvdHNbWzFdXSwgbW9kcGxvdHNbWzJdXSwgbW9kcGxvdHNbWzNdXSkKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdyb2JzID0gdG1wLCBuY29sID0gMywgYXMudGFibGUgPSBGQUxTRSkKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0RldmVsX21vZHVsZXNfQUVfcGxvdHMucGRmIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNzApCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdyb2JzID0gdG1wLCBuY29sID0gMywgYXMudGFibGUgPSBGQUxTRSkKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX01OX21vZF90c25lLnBkZiIsIGhlaWdodCA9IDYsIHdpZHRoID0gMTIpCm1vZHBsb3RzW1sxXV1bWzIyXV0gKyBtb2RwbG90c1tbMV1dW1s2XV0gfAogIG1vZHBsb3RzW1syXV1bWzIyXV0gKyBtb2RwbG90c1tbMl1dW1s2XV0gfAogIG1vZHBsb3RzW1szXV1bWzIyXV0gKyBtb2RwbG90c1tbM11dW1s2XV0KCmBgYAoKIyBBRSBvdmVyIHRpbWUKCldlIHBsb3QgdGhlIGF2ZXJhZ2UgZXhwcmVzc2lvbiBvZiBlYWNoIG1vZHVsZSBpbiB0aGUgdGhyZWUgc3RhZ2VzIGFuZCB0aGUgNSBicm9hZCBjZWxsIHR5cGUgY2x1c3RlcnMgcHJlc2VudCBpbiBhbGwgMyBzdGFnZXMuCgpgYGB7ciBBRS1vdmVyLXRpbWUsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEyfQojIG1vZHVsZSBhbm5vdGF0aW9ucwptb2RfYW5ub3QgPC0gcmVhZC5jc3YoIn4vc3BpbmFsX2NvcmRfcGFwZXIvYW5ub3RhdGlvbnMvR2dfZGV2ZWxfaW50X3NjV0dDTkFfbW9kdWxlX2Fubm90YXRpb24uY3N2IikgJT4lCiAgZHBseXI6Om11dGF0ZShtb2R1bGUgPSBzdHJfcmVwbGFjZV9hbGwobW9kdWxlLCAiXFxkezEsMn1cXF8iLCAiQUUiKSkKCm1ldGEgPC0gbGlzdCgpCgpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICBtZXRhW1tpXV0gPC0gbXkuc2VzW1tpXV1AbWV0YS5kYXRhICU+JQogICAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImNlbGxfSUQiKSAlPiUKICAgIGRwbHlyOjptdXRhdGUoY2VsbF9JRCA9IHBhc3RlMChjZWxsX0lELCAiXyIsIGkpKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoYygiY2VsbF9JRCIsICJicm9hZCIpKQp9CgptZXRhIDwtIGRvLmNhbGwocmJpbmQsIG1ldGEpCgojIG1lYW4gYXZlcmFnZSBleHByZXNzaW9uIGJ5IHN0YWdlCm1lYW5fQUUgPC0gYXZnLm1vZC5laWdlbmdlbmVzICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJjZWxsX0lEIikgJT4lCiAgZHBseXI6Om11dGF0ZShzdGFnZSA9IHN0cmluZ3I6OnN0cl9zdWIoY2VsbF9JRCwgLTEpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHN0YWdlID0gZmFjdG9yKHN0YWdlLCBsZXZlbHMgPSBjKDE6MyksIGxhYmVscyA9IGMoIkQwNSIsICJEMDciLCAiRDEwIikpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKG1ldGEsIGJ5ID0gImNlbGxfSUQiKSAlPiUKICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcygiY2VsbF9JRCIpICU+JQogIHRpZHlyOjp1bml0ZSgic3RhZ2VfY2wiLCBzdGFnZSwgYnJvYWQsIHNlcCA9ICJfIikgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHN0YWdlX2NsKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlX2VhY2gobWVhbikgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBnYXRoZXIoa2V5PSJtb2R1bGUiLCB2YWx1ZSA9ICJBRSIsIC1zdGFnZV9jbCkgJT4lCiAgZHBseXI6OmxlZnRfam9pbihtb2RfYW5ub3RbLCBjKDEsMyldLCBieSA9ICJtb2R1bGUiKSAlPiUKICB0aWR5cjo6c2VwYXJhdGUoInN0YWdlX2NsIiwgYygic3RhZ2UiLCAiYnJvYWQiKSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkgCiMgJT4lCiAgIyBkcGx5cjo6ZmlsdGVyKGJyb2FkICVpbiUgYygicHJvZ2VuaXRvcnMiLCAibmV1cm9ucyIsICJSUCIsICJGUCIsICJwZXJpY3l0ZXMiKSkKCmxhYmVsc19kb3RwbG90IDwtIHN0cmluZ3I6OnN0cl9yZW1vdmUobW9kdWxlc19pbl9vcmRlciwgIl5BRSIpCm5hbWVzKGxhYmVsc19kb3RwbG90KSA8LSBtb2R1bGVzX2luX29yZGVyCgpwbG90X2NsdXN0ZXJzIDwtIGMoInByb2dlbml0b3JzIiwgIm5ldXJvbnMiLCAiUlAiLCAiRlAiLCAicGVyaWN5dGVzIiwgIk9QQyIsICJNRk9MIiwgIm1pY3JvZ2xpYSIsICJibG9vZCIpCgptZWFuX21vZCA8LSBnZ3Bsb3QoZGF0YSA9IG1lYW5fQUUsCiAgYWVzKAogICAgeCA9IHN0YWdlLAogICAgeSA9IEFFLAogICAgY29sb3IgPSBmYWN0b3IoYnJvYWQsIGxldmVscyA9IHBsb3RfY2x1c3RlcnMpLAogICAgZ3JvdXAgPSBicm9hZCwKICAgIGxhYmVsID0gYW5ub3RhdGlvbgogICAgKQogICkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjbHVzdF9jb2wkY29sb3JbbWF0Y2gocGxvdF9jbHVzdGVycywgY2x1c3RfY29sJGJyb2FkKV0pICsKICB0aGVtZV9idygpICsKICAjIGZhY2V0IHdyYXAgd2l0aCByZW9yZGVyZWQgZmFjdG9ycwogIGZhY2V0X3dyYXAodmFycyhmYWN0b3IobW9kdWxlLCBsZXZlbHMgPSB1bmlxdWUobWVhbl9BRSRtb2R1bGUpW21vZHVsZV9vcmRlcl0pKSwKICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlX3kiLAogICAgICAgICAgICAgbnJvdyA9IDQsCiAgICAgICAgICAgICBuY29sID0gNikgKwogIGxhYnMoY29sb3IgPSAiYnJvYWQiKSArCiAgZ2d0aXRsZSgiQXZlcmFnZSBtb2R1bGUgZXhwcmVzc2lvbiBieSBzdGFnZSIpCgpwbG90bHk6OmdncGxvdGx5KG1lYW5fbW9kKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfbWVhbl9tb2RfQUUucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSAxMikKI1Bsb3Qgc3BsaXQgdHNuZQptZWFuX21vZApgYGAKCiMgVmxuUGxvdHMgb2YgYXZnLiBtb2R1bGUgZXhwLiBieSBzdGFnZSBhbmQgc2V1cmF0IGNsdXN0ZXIgCgojIyBjb2xvcmVkIGJ5IG1vZHVsZQoKYGBge3IgYXZlcmFnZS1tb2R1bGUtZXhwcmVzc2lvbi1wZXItY2x1c3RlciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTIwfQoKIyByZW9yZGVyIHNldXJhdCBjbHVzdGVycwpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICBteS5zZXNbW2ldXSRzZXVyYXRfY2x1c3RlcnMgPC0gZmFjdG9yKAogIG15LnNlc1tbaV1dJHNldXJhdF9jbHVzdGVycywKICBsZXZlbHMgPSBsZXZlbHMobXkuc2VzW1tpXV0kc2V1cmF0X2NsdXN0ZXJzKVthcy5pbnRlZ2VyKHN0cl9leHRyYWN0KG9yZF9sZXZlbHNbW2ldXSwgIlxcZHsxLDJ9JCIpKV0KICApCgp9Cgp2cGxvdHMgPC0gbGlzdCgpCgpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICAKICBtb2RzIDwtIGNvbG5hbWVzKG15LnNlc1tbaV1dW1tdXSlbZ3JlcCgiXkFFIixjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pKV0KICAKICB2cGxvdHNbW2ldXSA8LSBWbG5QbG90KAogICAgICAgIG15LnNlc1tbaV1dLAogICAgICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLAogICAgICAgIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsCiAgICAgICAgc3RhY2sgPSBUUlVFLCBmbGlwID0gVFJVRSwKICAgICAgICBjb2xzID0gc3Vic3RyaW5nKG1vZHMsIDMpW21vZHVsZV9vcmRlcl0pICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gImRhc2hlZCIpCiAgCn0KCnZwbG90c1tbMV1dCnZwbG90c1tbMl1dCnZwbG90c1tbM11dCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9BRV9ieV9jbHVzdGVyX21vZGNvbC5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAxMCkKdnBsb3RzW1sxXV0KdnBsb3RzW1syXV0KdnBsb3RzW1szXV0KYGBgCgojIyBjb2xvcmVkIGJ5IGNlbGwgdHlwZQoKYGBge3IgdmxuLWJ5LWNsdXN0ZXIsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0yMH0KY2x1c3RfY29sIDwtIHJlYWQuY3N2KCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zL2Jyb2FkX2NsdXN0ZXJfbWFya2VyX2NvbG9ycy5jc3YiKQoKdnBsb3RzX2lkIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgICAKICBtb2RzIDwtIGNvbG5hbWVzKG15LnNlc1tbaV1dW1tdXSlbZ3JlcCgiXkFFIixjb2xuYW1lcyhteS5zZXNbW2ldXVtbXV0pKV0KICAKICB2cGxvdHNfaWRbW2ldXSA8LSBWbG5QbG90KAogICAgbXkuc2VzW1tpXV0sCiAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwKICAgIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsCiAgICBzdGFjayA9IFRSVUUsIGZsaXAgPSBUUlVFLAogICAgZmlsbC5ieSA9ICJpZGVudCIsCiAgICBjb2xzID0gY29sX3RhYmxlW1tpXV0kY29sb3JbYXMuaW50ZWdlcihzdHJfZXh0cmFjdChvcmRfbGV2ZWxzW1tpXV0sICJcXGR7MSwyfSQiKSldKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9ICJkYXNoZWQiKQp9Cgp2cGxvdHNfaWRbWzFdXQp2cGxvdHNfaWRbWzJdXQp2cGxvdHNfaWRbWzNdXQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfQUVfYnlfY2x1c3Rlcl9jbHVjb2wucGRmIiwgaGVpZ2h0ID0gMjAsIHdpZHRoID0gMTApCnZwbG90c19pZFtbMV1dCnZwbG90c19pZFtbMl1dCnZwbG90c19pZFtbM11dCmBgYAoKIyBNTiBtb2R1bGVzCgpGb3IgdGhlIGZpZ3VyZXMgd2Ugc3BlY2lmaWNhbGx5IHNlbGVjdCB0aGUgdHdvIE1OIG1vZHVsZXMgYW5kIHBsb3QgdGhlbSBhcyBWbG4gcGxvdHMuCgpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTV9CnRtcCA8LSBhdmcubW9kLmVpZ2VuZ2VuZXMgCiAgCmlkZW50aWNhbChyb3duYW1lcyh0bXApLCBjb2xuYW1lcyhteS5zZWMpKQojIGFkZCBtZXRhIGRhdGEgdG8gdGhlIGludCBzZXVyYXQgb2JqZWN0Cm15LnNlYyA8LSBBZGRNZXRhRGF0YShteS5zZWMsIHRtcCkKbXkuc2VjIDwtIEFkZE1ldGFEYXRhKG15LnNlYywgbXkubWV0YW1bYygiZmluZSIsICJzYW1wbGVfY2VsbHR5cGUiKV0pCgpjdXN0b21fb3JkZXIgPC0gYyhwYXN0ZShuYW1lcyhvcmRfbGV2ZWxzKVsxXSwgb3JkX2xldmVsc1tbMV1dLCBzZXAgPSAnXycpLAogICAgICAgICAgICAgICAgICBwYXN0ZShuYW1lcyhvcmRfbGV2ZWxzKVsyXSwgb3JkX2xldmVsc1tbMl1dLCBzZXAgPSAnXycpLAogICAgICAgICAgICAgICAgICBwYXN0ZShuYW1lcyhvcmRfbGV2ZWxzKVszXSwgb3JkX2xldmVsc1tbM11dLCBzZXAgPSAnXycpKQoKbXkuc2VjJHNhbXBsZV9jZWxsdHlwZSA8LSBmYWN0b3IoCiAgbXkuc2VjJHNhbXBsZV9jZWxsdHlwZSwKICBsZXZlbHMgPSBjdXN0b21fb3JkZXIKICApCgp2bG5faW5kIDwtIFZsblBsb3QobXkuc2VjLAogICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJBRWRhcmtyZWQiLCAiQUVncmVlbiIpLAogICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAic2FtcGxlX2NlbGx0eXBlIiwKICAgICAgICAgICAgICAgICAgIHN0YWNrID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIGZsaXAgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgY29scyA9IGMoImRhcmtyZWQiLCAiZ3JlZW4iKSxwdC5zaXplID0gMQogICAgICAgICAgICAgICAgICApICsKICAgIE5vTGVnZW5kKCkKCnZsbl9pbmQKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfQUVfTU5zLnBkZiIsIGhlaWdodCA9IDcsIHdpZHRoID0gMjApCnZsbl9pbmQKYGBgCgpgYGB7cn0KVmxuUGxvdCgKICAgIG15LnNlYywKICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLAogICAgZ3JvdXAuYnkgPSAic2FtcGxlX2NlbGx0eXBlIiwKICAgIHN0YWNrID0gVFJVRSwgZmxpcCA9IFRSVUUsCiAgICBjb2xzID0gc3Vic3RyaW5nKG1vZHMsIDMpW21vZHVsZV9vcmRlcl0pICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gImRhc2hlZCIpCgoKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX0FFX2J5X2NsdXN0ZXJfaW50ZWdyYXRlZF9kYXRhLnBkZiIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDMwKQpWbG5QbG90KAogICAgbXkuc2VjLAogICAgZmVhdHVyZXMgPSBtb2RzW21vZHVsZV9vcmRlcl0sCiAgICBncm91cC5ieSA9ICJzYW1wbGVfY2VsbHR5cGUiLAogICAgc3RhY2sgPSBUUlVFLCBmbGlwID0gVFJVRSwKICAgIGNvbHMgPSBzdWJzdHJpbmcobW9kcywgMylbbW9kdWxlX29yZGVyXSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsdHkgPSAiZGFzaGVkIikKCgpgYGAKCiMgYXZnLiBtb2R1bGUgZXhwLiBieSBzdGFnZQoKYGBge3IgQUUtYnktc3RhZ2UsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0yMH0KdjEgPC0gVmxuUGxvdChteS5zZWMsCiAgICAgICAgZmVhdHVyZXMgPSBtb2RzW21vZHVsZV9vcmRlcl0sIAogICAgICAgIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKQp2MQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfQUVfYnlfc3RhZ2UucGRmIiwgaGVpZ2h0ID0gMjAsIHdpZHRoID0gMjApCnYxCgpgYGAKCiMgbW9kdWxlIEdPIHRlcm1zIGJhcnBsb3RzCgpgYGB7cn0KIyBMb2FkIHRoZSB0b3AgNTAgKGJ5IGxpbW1hOjp0b3BHTykgR28gdGVybSB0YWJsZSBsaXN0IGZyb20gc2NXR0NOQV9HZ19kZXZlbF9pbnQuUm1kCmdvdGVybXMgPC0gcmVhZFJEUygifi9zcGluYWxfY29yZF9wYXBlci9vdXRwdXQvR2dfZGV2ZWxfaW50X21vZHVsZV9HT1Rlcm1zXzI1MDcyMy5yZHMiKQojIExvYWQgdGhlIGRldmVsIG1vZHVsZSBhbm5vdGF0aW9ucyAKbW9kdWxlcyA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9HZ19kZXZlbF9pbnRfc2NXR0NOQV9tb2R1bGVfYW5ub3RhdGlvbi5jc3YiKQojIFNldCBsaXN0IG5hbWVzIGFzIG1vZHVsZSBudW1iZXIgYW5kIG5hbWUgKGUuZy4gIjFfYmxhY2siKQpuYW1lcyhnb3Rlcm1zKSA8LSBtb2R1bGVzJG1vZHVsZQoKcGxvdF9saXN0IDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShnb3Rlcm1zKSkgewogICMgU2VsZWN0IHRoZSBkYXJrcmVkIG1vZHVsZSAobW90b3IgbmV1cm9uL3RyYW5zbWVtYnJhbmUgdHJhbnNwb3J0KQogIHRvcGxvdCA8LSBnb3Rlcm1zW1tpXV0KICB0ZXJtcyA8LSBhcy5jaGFyYWN0ZXIodG9wbG90JFRlcm0pCiAgCiAgI1Bsb3QgdGhlIGJhcnBsb3QhCiAgcGxvdF9saXN0W1tpXV0gPC0gZ2dwbG90KHRvcGxvdCwgYWVzKHg9VGVybSwgeT0tbG9nMTAoUC5ERSksIGZpbGw9T250KSkgKwogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBzdHJfcmVtb3ZlKG5hbWVzKGdvdGVybXMpW2ldLCAiXlxcZHsxLDJ9XyIpKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpLAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDUpKSArCiAgICBsYWJzKHRpdGxlPXBhc3RlMCgiVG9wIEdPVGVybSBvZiBtb2R1bGUgIiwgbmFtZXMoZ290ZXJtcylbaV0pKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKwogICAgdGhlbWVfY293cGxvdCgpCn0KCnJtKHRvcGxvdCwgdGVybXMpCgpuYW1lcyhwbG90X2xpc3QpIDwtIG1vZHVsZXMkbW9kdWxlCgojIEV4cG9ydCBQREYKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvR09fdGVybV9iYXJwbG90X3NjV0dDTkFfR2dfZGV2ZWxfbW9kdWxlcy5wZGYiLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSA3KQpmb3IgKGkgaW4gc2VxKGdvdGVybXMpKSB7CiAgZ3JpZC5hcnJhbmdlKHBsb3RfbGlzdFtbaV1dKQp9CmRldi5vZmYoKQoKYGBgCgoKYGBge3J9CiMgRGF0ZSBhbmQgdGltZSBvZiBSZW5kZXJpbmcKU3lzLnRpbWUoKQoKc2Vzc2lvbkluZm8oKQpgYGAKCg==